package com.woniu.service.impl;

import com.alibaba.fastjson.JSON;
import com.woniu.entity.KillBook;
import com.woniu.entity.KillOrder;
import com.woniu.producer.TopicProducer;
import com.woniu.service.SecKillService;
import com.woniu.util.ResponseData;
import com.woniu.util.ResponseEnum;
import com.woniu.util.SnowflakeIdGenerator;
import com.woniu.util.UserContext;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
@AllArgsConstructor
public class SecKillServiceImpl implements SecKillService {
    private RedisTemplate<String,Object> redisTemplate;
    private TopicProducer topicProducer;
    /**
     * 添加秒杀书籍
     * 1.写入秒杀表
     * 2.写人redis
     * 3.
     * @param killBook
     * @return
     */
    @Override
    public ResponseData<?> addKillBook(KillBook killBook) {
        //要求秒杀书籍只会提前五分钟进入redis
        redisTemplate.opsForHash().put("SECKILLSERVICEIMPL:ADDKILLBOOK",killBook.getBookId()+"",killBook);
        return ResponseData.ok();
    }

    @Override
    public ResponseData<?> queryKillBook() {
        List<Object> killBook = redisTemplate.opsForHash().values("SECKILLSERVICEIMPL:ADDKILLBOOK");
        return ResponseData.ok(killBook);
    }

    @Override
    public ResponseData<?> queryDetail(Integer bookId) {
        KillBook killBook = (KillBook) redisTemplate.opsForHash().get("SECKILLSERVICEIMPL:ADDKILLBOOK", bookId+"");
        return ResponseData.ok(killBook);
    }

    /**
     * 用户抢购系统
     * 1.判断库存是否足够
     *      不够：返回
     *      够：执行抢购命令
     * 2.减库存
     * 3.生成秒杀订单
     *
     *
     * synchronized
     *
     * 分布式锁
     * 应用场景:1：项目要集群部署 2：代码中出现线程不安全代码
     * 实现方式：利用redis实现分布式锁。通过setnx指令的特点实现的。
     *         特点：当用setnx指令放入一个数据时，会先判断该key是否存在，
     *         如果存在不能放入，不存在可以放入。
     *
     *
     * @param bookId
     * @return
     */
    @Override
    public  ResponseData<?> startKill(Integer bookId) {
        String uuid = UUID.randomUUID().toString();
        try {
            //本质是封装的setnx指令

            Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent("lock", uuid);
            KillBook killBook = null;
            if (aBoolean) {
                killBook = (KillBook) redisTemplate.opsForHash().get("SECKILLSERVICEIMPL:ADDKILLBOOK", bookId + "");
                if (killBook.getKillNum() <= 0) {
                    //直接返回
                    return ResponseData.fail(ResponseEnum.KILL_END);
                }
                //减库存
                killBook.setKillNum(killBook.getKillNum() - 1);
                System.out.println("剩余" + killBook.getKillNum());
                redisTemplate.opsForHash().put("SECKILLSERVICEIMPL:ADDKILLBOOK", bookId + "", killBook);

            } else {
                return ResponseData.fail(ResponseEnum.KILL_END);
            }


            //3.生成秒杀订单(不可同步写入数据库，改用rabbitmq异步写入mysql)
//        KillOrder killOrder = new KillOrder();
            KillOrder killOrder = new KillOrder();
            SnowflakeIdGenerator sfg = new SnowflakeIdGenerator();
            String orderNum = String.valueOf(sfg.nextId());
            killOrder.setKillOrderNum(orderNum);
            killOrder.setBookId(bookId);
            Integer userId = UserContext.getUserId();
            killOrder.setUserId(userId);
            killOrder.setPrice(killBook.getDiscountPrice());
            killOrder.setKillNum(1);
            killOrder.setStatus(1 + "");
            String jsonStr = JSON.toJSONString(killOrder);
            //解决重复消费问题 把订单编号存入redis
            redisTemplate.opsForValue().set(orderNum,orderNum,5, TimeUnit.MINUTES);
            topicProducer.send("kellBook", jsonStr);
            return ResponseData.ok();
        }catch (Exception e){
            throw new RuntimeException();
        } finally {
            //释放锁
            if (uuid.equals(redisTemplate.opsForValue().get("lock"))){
                redisTemplate.delete("lock");
            }
        }

    }
}
